package com.yunquan.ohana.utils

import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.media.MediaMetadataRetriever
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import com.yunquan.ohana.entity.PhotosEntity
import java.io.File

object MediaUtils {

    private val mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    private val mVideoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI

    /**
     * 根据路径获取图片
     */
    fun getImageById(context: Context, id: String): PhotosEntity? {
        val imagesUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        val projection = arrayOf(
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.MIME_TYPE,
            MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
            MediaStore.Images.Media.DURATION,
            MediaStore.Images.Media.DATE_ADDED,
            MediaStore.Images.Media.DATE_MODIFIED,
            MediaStore.Images.Media.WIDTH,
            MediaStore.Images.Media.HEIGHT,
            MediaStore.Images.Media.SIZE,
        )
        val orderBy = "${MediaStore.Images.Media.DATE_MODIFIED} DESC"
        val dataList = mutableListOf<PhotosEntity>()
        context.contentResolver.query(imagesUri, projection, MediaStore.Images.Media._ID + "='" + id + "'", null, orderBy).use { c ->
            if (c != null) {
                dataList.addAll(toEntity(context, 1, c))
            }
        }
        return if (dataList.isEmpty()) null else dataList.first()
    }

    /**
     * 根据路径获取视频
     */
    fun getVideoById(context: Context, id: String): PhotosEntity? {
        val videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        val projection = arrayOf(
            MediaStore.Video.Media._ID,
            MediaStore.Video.Media.DATA,
            MediaStore.Video.Media.MIME_TYPE,
            MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
            MediaStore.Video.Media.DURATION,
            MediaStore.Video.Media.DATE_ADDED,
            MediaStore.Video.Media.DATE_MODIFIED,
            MediaStore.Video.Media.WIDTH,
            MediaStore.Video.Media.HEIGHT,
            MediaStore.Video.Media.SIZE,
        )
        val orderBy = "${MediaStore.Video.Media.DATE_MODIFIED} DESC"
        val dataList = mutableListOf<PhotosEntity>()
        context.contentResolver.query(videoUri, projection, MediaStore.Video.Media._ID + "='" + id + "'", null, orderBy).use { c ->
            if (c != null) {
                dataList.addAll(toEntity(context, 2, c))
            }
        }
        return if (dataList.isEmpty()) null else dataList.first()
    }

    /**
     * 获取所有数量
     */
    fun getAllCount(context: Context): Int {
        var count = 0
        val projection = arrayOf(
            MediaStore.Files.FileColumns._ID,
        )
        try {
            context.contentResolver.query(mImageUri, projection, null, null, null).use {
                count += it?.count ?: 0
            }
        } catch (_: Exception) {
        }
        try {
            context.contentResolver.query(mVideoUri, projection, null, null, null).use {
                count += it?.count ?: 0
            }
        } catch (_: Exception) {
        }
        return count
    }

    /**
     * 获取分组数量
     */
    fun getGroupCount(context: Context, group: String): Int {
        var count = 0
        val projection = arrayOf(
            MediaStore.Files.FileColumns._ID,
        )
        try {
            context.contentResolver.query(mImageUri, projection, MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME + "=?", arrayOf(group), null).use {
                count += it?.count ?: 0
            }
        } catch (_: Exception) {
        }
        try {
            context.contentResolver.query(mVideoUri, projection, MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME + "=?", arrayOf(group), null).use {
                count += it?.count ?: 0
            }
        } catch (_: Exception) {
        }
        return count
    }

    /**
     * 获取所有分组
     */
    fun getAllGroup(context: Context): List<String> {
        val groupList = mutableListOf<String>()
        val imageGroup = getImageGroup(context)
        val videoGroup = getVideoGroup(context)
        for (e in imageGroup) {
            if (!groupList.contains(e)) {
                groupList.add(e)
            }
        }
        for (e in videoGroup) {
            if (!groupList.contains(e)) {
                groupList.add(e)
            }
        }
        return groupList
    }

    /**
     * 获取图片分组
     */
    fun getImageGroup(context: Context): List<String> {
        val groupList = mutableListOf<String>()
        val projection = arrayOf(
            MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME,
        )
        val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val bundle = Bundle().apply {
                putStringArray(
                    ContentResolver.QUERY_ARG_SORT_COLUMNS,
                    arrayOf(MediaStore.Files.FileColumns._ID)
                )
                putInt(
                    ContentResolver.QUERY_ARG_SORT_DIRECTION,
                    ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
                )
                putString(
                    ContentResolver.QUERY_ARG_SQL_GROUP_BY,
                    MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME
                )
            }
            context.contentResolver?.query(mImageUri, projection, bundle, null)
        } else {
            val orderBy = "${MediaStore.Files.FileColumns._ID} DESC GROUP BY ${MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME}"
            context.contentResolver?.query(mImageUri, projection, null, null, orderBy)
        }
        cursor.use {
            val groupIndex = it?.getColumnIndexOrThrow(MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME) ?: 0
            while (it?.moveToNext() == true) {
                groupList.add(it.getString(groupIndex))
            }
        }
        return groupList
    }

    /**
     * 获取视频分组
     */
    fun getVideoGroup(context: Context): List<String> {
        val groupList = mutableListOf<String>()
        val projection = arrayOf(
            MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME,
        )
        val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val bundle = Bundle().apply {
                putStringArray(
                    ContentResolver.QUERY_ARG_SORT_COLUMNS,
                    arrayOf(MediaStore.Files.FileColumns._ID)
                )
                putInt(
                    ContentResolver.QUERY_ARG_SORT_DIRECTION,
                    ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
                )
                putString(
                    ContentResolver.QUERY_ARG_SQL_GROUP_BY,
                    MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME
                )
            }
            context.contentResolver?.query(mVideoUri, projection, bundle, null)
        } else {
            val orderBy = "${MediaStore.Files.FileColumns._ID} DESC GROUP BY ${MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME}"
            context.contentResolver?.query(mVideoUri, projection, null, null, orderBy)
        }
        cursor.use {
            val groupIndex = it?.getColumnIndexOrThrow(MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME) ?: 0
            while (it?.moveToNext() == true) {
                groupList.add(it.getString(groupIndex))
            }
        }
        return groupList
    }

    /**
     * 分页获取媒体
     */
    fun page(context: Context, group: String?, page: Int, pageSize: Int): List<PhotosEntity> {
        L.i("查询条件：$group,$page,$pageSize")
        val images = imagePage(context, group, page, pageSize)
        L.i("查询图片：${images.size}")
        val videos = videoPage(context, group, page, pageSize)
        L.i("查询视频：${videos.size}")
        val dataList = mutableListOf<PhotosEntity>()
        dataList.addAll(images)
        dataList.addAll(videos)
        dataList.sortByDescending { it.dateTime }
        return dataList
    }

    /**
     * 分页获取视频
     */
    fun videoPage(context: Context, group: String?, page: Int, pageSize: Int): List<PhotosEntity> {
        val projection = arrayOf(
            MediaStore.Video.Media._ID,
            MediaStore.Video.Media.DATA,
            MediaStore.Video.Media.MIME_TYPE,
            MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
            MediaStore.Video.Media.DURATION,
            MediaStore.Video.Media.DATE_ADDED,
            MediaStore.Video.Media.DATE_MODIFIED,
            MediaStore.Video.Media.WIDTH,
            MediaStore.Video.Media.HEIGHT,
            MediaStore.Video.Media.SIZE,
        )
        val selection: String
        val selectionArgs: Array<String>

        if (StringUtils.isEmpty(group)) {
            selection = ""
            selectionArgs = arrayOf()
        } else {
            selection = "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} = ?"
            selectionArgs = arrayOf(group!!)
        }

        val limitCount = pageSize
        val offset = page * pageSize
        val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val bundle = Bundle().apply {
                putInt(
                    ContentResolver.QUERY_ARG_SORT_DIRECTION,
                    ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
                )
                putStringArray(
                    ContentResolver.QUERY_ARG_SORT_COLUMNS,
                    arrayOf(MediaStore.Video.Media._ID)
                )
                putString(ContentResolver.QUERY_ARG_SQL_LIMIT, "$limitCount OFFSET $offset")
                putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
                putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs)
            }
            context.contentResolver.query(mVideoUri, projection, bundle, null)
        } else {
            val orderBy = "${MediaStore.Video.Media.DATE_MODIFIED} DESC LIMIT $limitCount OFFSET $offset"
            context.contentResolver.query(mVideoUri, projection, selection, selectionArgs, orderBy)
        }

        val dataList = mutableListOf<PhotosEntity>()
        cursor?.use { c ->
            val list = toEntity(context, 2, c)
            dataList.addAll(list)
        }
        return dataList
    }

    /**
     * 分页获取图片
     */
    fun imagePage(context: Context, group: String?, page: Int, pageSize: Int): List<PhotosEntity> {
        val projection = arrayOf(
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.MIME_TYPE,
            MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
            MediaStore.Images.Media.DURATION,
            MediaStore.Images.Media.DATE_ADDED,
            MediaStore.Images.Media.DATE_MODIFIED,
            MediaStore.Images.Media.WIDTH,
            MediaStore.Images.Media.HEIGHT,
            MediaStore.Images.Media.SIZE,
        )
        val selection: String
        val selectionArgs: Array<String>

        if (StringUtils.isEmpty(group)) {
            selection = ""
            selectionArgs = arrayOf()
        } else {
            selection = "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} = ?"
            selectionArgs = arrayOf(group!!)
        }

        val limitCount = pageSize
        val offset = page * pageSize
        val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val bundle = Bundle().apply {
                putInt(
                    ContentResolver.QUERY_ARG_SORT_DIRECTION,
                    ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
                )
                putStringArray(
                    ContentResolver.QUERY_ARG_SORT_COLUMNS,
                    arrayOf(MediaStore.Images.Media._ID)
                )
                putString(ContentResolver.QUERY_ARG_SQL_LIMIT, "$limitCount OFFSET $offset")
                putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
                putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs)
            }
            context.contentResolver.query(mImageUri, projection, bundle, null)
        } else {
            val orderBy = "${MediaStore.Images.Media.DATE_MODIFIED} DESC LIMIT $limitCount OFFSET $offset"
            context.contentResolver.query(mImageUri, projection, selection, selectionArgs, orderBy)
        }

        val dataList = mutableListOf<PhotosEntity>()
        cursor?.use { c ->
            val list = toEntity(context, 2, c)
            dataList.addAll(list)
        }
        return dataList
    }

    /**
     * 通过游标获取实体
     */
    fun toEntity(context: Context, mediaType: Int, it: Cursor): List<PhotosEntity> {
        val dataList = mutableListOf<PhotosEntity>()
        // ID
        val idColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns._ID
                2 -> MediaStore.Video.VideoColumns._ID
                else -> MediaStore.MediaColumns._ID
            }
        )
        // 路径
        val pathColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.DATA
                2 -> MediaStore.Video.VideoColumns.DATA
                else -> MediaStore.MediaColumns.DATA
            }
        )
        // 类型
        val typeColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.MIME_TYPE
                2 -> MediaStore.Video.VideoColumns.MIME_TYPE
                else -> MediaStore.MediaColumns.MIME_TYPE
            }
        )
        // 类别
        val groupColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME
                2 -> MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME
                else -> MediaStore.MediaColumns.BUCKET_DISPLAY_NAME
            }
        )
        // 时长
        val durationColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.DURATION
                2 -> MediaStore.Video.VideoColumns.DURATION
                else -> MediaStore.MediaColumns.DURATION
            }
        )
        // 添加时间
        val dateColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.DATE_ADDED
                2 -> MediaStore.Video.VideoColumns.DATE_ADDED
                else -> MediaStore.MediaColumns.DATE_ADDED
            }
        )
        // 修改时间
        val updateDateColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.DATE_MODIFIED
                2 -> MediaStore.Video.VideoColumns.DATE_MODIFIED
                else -> MediaStore.MediaColumns.DATE_MODIFIED
            }
        )
        // 高
        val heightColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.HEIGHT
                2 -> MediaStore.Video.VideoColumns.HEIGHT
                else -> MediaStore.MediaColumns.HEIGHT
            }
        )
        // 宽
        val widthColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.WIDTH
                2 -> MediaStore.Video.VideoColumns.WIDTH
                else -> MediaStore.MediaColumns.WIDTH
            }
        )
        // 大小
        val sizeColumn = it.getColumnIndexOrThrow(
            when (mediaType) {
                1 -> MediaStore.Images.ImageColumns.SIZE
                2 -> MediaStore.Video.VideoColumns.SIZE
                else -> MediaStore.MediaColumns.SIZE
            }
        )
        while (it.moveToNext()) {
            val id = it.getLong(idColumn)
            val path = it.getString(pathColumn)
            val type = it.getString(typeColumn)
            val group = it.getString(groupColumn)
            var duration = it.getLong(durationColumn)
            val date = it.getLong(dateColumn)
            val updateDate = it.getLong(updateDateColumn)
            val height = it.getInt(heightColumn)
            val width = it.getInt(widthColumn)
            val size = it.getInt(sizeColumn)
            val file = File(path)
            if (!file.exists() || !file.canRead() || file.length() <= 0) {
                continue
            }
            val data = if (type.contains("image")) {
                PhotosEntity(
                    id = id,
                    path = path,
                    thumbnail = null,
                    type = type,
                    group = group,
                    duration = duration,
                    dateTime = date,
                    updateTime = updateDate,
                    width = width,
                    height = height,
                    size = size
                )
            } else {
                val thumbnail = getThumbnail(context, id, path)
                duration = if (duration == 0L) getVideoDuration(path) else duration
                PhotosEntity(
                    id = id,
                    path = path,
                    thumbnail = thumbnail,
                    type = type,
                    group = group,
                    duration = duration,
                    dateTime = date,
                    updateTime = updateDate,
                    width = width,
                    height = height,
                    size = size
                )
            }
            dataList.add(data)
        }
        return dataList
    }

    /**
     * 获取视频封面
     */
    private fun getThumbnail(context: Context, id: Long, path: String): String? {
        val file = File(path)
        if (!file.exists()) {
            return null
        }
        val name = file.nameWithoutExtension
        val fileName = "$name.jpg"
        // 如果存在则直接返回
        val oldFile = File(FileUtils.getFilePath(), fileName)
        if (oldFile.exists()) {
            return oldFile.absolutePath
        }
        // 尝试获取视频封面1
        try {
            val bitmap = MediaStore.Video.Thumbnails.getThumbnail(context.contentResolver, id, MediaStore.Video.Thumbnails.MICRO_KIND, null)
            val newFile = ImageUtils.saveBitmapToStorage(bitmap, fileName)
            return newFile?.absolutePath
        } catch (e: Exception) {
            L.e("使用MediaStore获取视频封面报错", e)
        }
        // 尝试获取视频封面2
        val retriever = MediaMetadataRetriever()
        return try {
            retriever.setDataSource(path)
            val bitmap = retriever.frameAtTime ?: return null
            val newFile = ImageUtils.saveBitmapToStorage(bitmap, fileName)
            newFile?.absolutePath
        } catch (e: IllegalArgumentException) { // 数据源错误
            L.e("数据源错误：\n${path}\n", e)
            null
        } catch (e: RuntimeException) { // retriever 解析错误
            L.e("解析错误：\n${path}\n", e)
            null
        } finally {
            retriever.release()
        }
    }


    /**
     * 获取视频时长 (单位: 毫秒)
     * @param path 视频 Uri
     */
    private fun getVideoDuration(path: String): Long {
        return try {
            MediaMetadataRetriever().use { retriever ->
                retriever.setDataSource(path)
                retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
            }
        } catch (e: IllegalArgumentException) { // 可能是无效文件路径
            e.printStackTrace()
            0L
        } catch (e: RuntimeException) { // retriever 解析失败
            e.printStackTrace()
            0L
        }
    }
}